package App.OOSerVice.service.thread;

import App.Model.ArisuDB;
import App.Model.Interface.MessageType;
import App.Model.Tool.DosTool;
import App.Model.User;
import App.Model.message.Message;
import App.OOSerVice.service.OOService;
import App.OOSerVice.service.thread.manage.ManageStoCThread;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

//监听客户端的线程
public class StoCThread extends Thread {
    private Socket socket;
    private String userId;

    public StoCThread(String userId, Socket socket) {
        this.userId = userId;
        this.socket = socket;
    }

    @Override
    public void run() {
        //服务器线程的优化:将轻量化的数据库等读取时间较长的放在一个单独的线程中运行,
                //所有的请求打包成队列,在数据库读取期间继续处理队列中的请求,这样是否会提高该线程的使用效率
        System.out.println("userId:" + userId + " 链接成功");
        while (true) {
            try {
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Message message = (Message) ois.readObject();
                String msgType = message.getMesType();
                //处理客户端信息
                if (msgType.equals(MessageType.MESSAGE_GET_ONLINE_LIST)) {
                    //发送客户端在线列表 列表格式:"100 200 300 ... "
                    System.out.println(userId + " 对 " + message.getGetter() + " 请求更新用户列表");
                    StringBuilder data = ArisuDB.getAllUserToString();
                    data.append(ManageStoCThread.getOnlineList());
                    //System.out.println(data);
                    //message构造
                    Message returnMessage = new Message();
                    returnMessage.setContent(data.toString());
                    returnMessage.setMesType(MessageType.MESSAGE_SET_ONLINE_LIST);
                    returnMessage.setGetter(message.getSender());
//                    //发送
//                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
//                    oos.writeObject(returnMessage);
                    //由于有用户上线,对其他在线用户进行消息更新
                    HashMap<String, StoCThread> manage = ManageStoCThread.getManage();
                    Iterator<String> iterator = manage.keySet().iterator();
                    while (iterator.hasNext()) {
                        String next = iterator.next().toString();
                        ObjectOutputStream oos =
                                new ObjectOutputStream(manage.get(next).getSocket().getOutputStream());
                        oos.writeObject(returnMessage);
                    }
                } else if (msgType.equals(MessageType.MESSAGE_COMM_MSG)) {
                    //用户间通话交流
                    StoCThread aimThread = ManageStoCThread.getStoCThread(message.getGetter());
                    //发送时应该使用目标对象所使用的socket对象进行发送
                    if (aimThread != null) {
                        ObjectOutputStream oos = new ObjectOutputStream(aimThread.getSocket().getOutputStream());
                        oos.writeObject(message);
                    } else {//发送失败时返回信息
                        if (ArisuDB.isExistUser(message.getGetter())) {//判断该用户是否已注册,已注册就更改为发送离线消息
                            //message.setMesType(MessageType.MESSAGE_COMM_MSG_OFFLINE);
                            ArisuDB.putOfflineMessage(message.getGetter(), message);
                        } else {
                            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                            message.setGetter(message.getSender());
                            message.setSender("服务器");
                            message.setContent("发送失败,用户参数错误");
                            oos.writeObject(message);
                        }
                    }
                } else if (msgType.equals(MessageType.MESSAGE_COMM_MSG_ALL)) {
                    //群发消息
                    //遍历哈希Map
                    System.out.println(message.getSender() + "发送全局消息: " + message.getContent());
                    HashMap<String, StoCThread> manage = ManageStoCThread.getManage();
                    Iterator<String> iterator = manage.keySet().iterator();
                    while (iterator.hasNext()) {
                        String next = iterator.next().toString();
                        ObjectOutputStream oos =
                                new ObjectOutputStream(manage.get(next).getSocket().getOutputStream());
                        oos.writeObject(message);
                    }
                } else if (msgType.equals(MessageType.MESSAGE_GET_MSG_NOW_USER)) {
                    //获取某个用户信息
                    System.out.println(message.getSender() + "请求获取 " + message.getContent() + " 的用户信息");
                    //找到用户信息(这里用json单纯练手,因为头像文件还需要单独用文件传输)
                    User aimUser = User.DeepCopy(ArisuDB.getUserMap().get(message.getContent()));
                    Message returnMessage = new Message();
                    returnMessage.setGetter(message.getSender());
                    returnMessage.setSender(message.getGetter());
                    returnMessage.setMesType(MessageType.MESSAGE_GET_MSG_NOW_USER);
                    returnMessage.setFileBytes(ArisuDB.getUserHeaderAtFile(aimUser));//将头像文件读取后放入message包中
                    if(!userId.equals(message.getContent())){//不能给其他人该用户的密码
                        aimUser.setPassword("无权查看");
                    }
                    String jUser = JSON.toJSONString(aimUser, SerializerFeature.WriteMapNullValue);//使序列化包含空值
                    returnMessage.setContent(jUser);
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(returnMessage);

                } else if (msgType.equals(MessageType.MESSAGE_COMM_MSG_OFFLINE)) {
                    //离线消息请求(服务器发送回去)
                    List<Message> offlineMessage = ArisuDB.getOfflineMessage(message.getSender());//获取该用户的离线消息并返回
                    message.setMessages(offlineMessage);
                    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                    oos.writeObject(message);
                    System.out.println("用户 " + message.getSender() + " 正在获取离线消息");
                    //由于用户已经获取,在数据库中将其删除
                    ArisuDB.cleanNewMessageData(userId);
                } else if(msgType.equals(MessageType.MESSAGE_SET_MSG_USER)){
                    //更改用户基础信息请求
                    System.out.println(message.getSender() + " 请求更改用户资料");
                    User fixUser = JSON.parseObject(message.getContent(),User.class);
                    System.out.println(message.getContent());
                    ArisuDB.getUserMap().put(userId,fixUser);//向ArisuDB存储入新的User
                    if(message.getDest() != null){
                        System.out.println("更新用户头像");
                        ArisuDB.saveUserHeaderToFile(message,fixUser.getHeaderUrl(),userId);//将头像文件储存到文件中
                    }
                    ArisuDB.saveUserMapToFile();//保存到文件中
                    OOService.validUsers.put(userId,fixUser);

                } else if (msgType.equals(MessageType.MESSAGE_FILE_MSG)) {
                    //文件暂存到服务器
                    ArisuDB.saveServiceTempSaveFile(message);
                    message.setFileBytes(null);
                    message.setMesType(MessageType.MESSAGE_FILE_MSG_CONFORM);

                    //发送文件提醒
                    StoCThread stoCThread = ManageStoCThread.getStoCThread(message.getGetter());//获取收件人的线程
                    if(stoCThread != null){//在线就直接发送
                        ObjectOutputStream oos = new ObjectOutputStream(stoCThread.getSocket().getOutputStream());
                        oos.writeObject(message);
                    }else{//不在线时的离线消息
                        if (ArisuDB.isExistUser(message.getGetter())) {//判断该用户是否已注册,已注册就更改为发送离线消息
                            //message.setMesType(MessageType.MESSAGE_COMM_MSG_OFFLINE);
                            ArisuDB.putOfflineMessage(message.getGetter(), message);
                        } else {
                            ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                            message.setGetter(message.getSender());
                            message.setSender("服务器");
                            message.setContent("发送失败,用户参数错误");
                            oos.writeObject(message);
                        }
                    }
                    System.out.println
                            ("用户: " + message.getSender() + " 向用户 " + message.getGetter() + " 发送了一份文件 " + message.getSrc());
                } else if (msgType.equals(MessageType.MESSAGE_FILE_MSG_GET)
                        || msgType.equals(MessageType.MESSAGE_FILE_MSG_NOTGET)) {
                    System.out.print
                            ("用户: " + message.getSender() + " 接受了一份 " + message.getGetter() + " 发送的文件");
                    //System.out.println(JSON.toJSONString(message));
                    //提取文件
                    Message returnMessage = ArisuDB.readServiceTempSaveFile(message);
                    //文件传输
                    ObjectOutputStream oos = new ObjectOutputStream(getSocket().getOutputStream());
                    oos.writeObject(returnMessage);
                    //给原发送发送已接受或者已拒绝消息
                    Message rMessage = new Message();
                    rMessage.setSender(message.getSender());
                    rMessage.setGetter(message.getGetter());
                    rMessage.setSendTime(new Date());
                    rMessage.setMesType(MessageType.MESSAGE_COMM_MSG_MENTION);
                    String fileName = DosTool.SplitFileName(message.getDest());
                    if(msgType.equals(MessageType.MESSAGE_FILE_MSG_GET)){
                        rMessage.setContent(message.getSender() + " 接受了文件 " + fileName);
                    }else {
                        rMessage.setContent(message.getSender() + " 拒绝了文件 " + fileName);
                    }
                    if(ManageStoCThread.getStoCThread(message.getGetter()) == null){
                        //用户不存在时,改为离线消息
                        ArisuDB.putOfflineMessage(message.getSender(),rMessage);
                    }else{
                        StoCThread stoCThread = ManageStoCThread.getStoCThread(message.getGetter());
                        ObjectOutputStream newOos = new ObjectOutputStream(stoCThread.getSocket().getOutputStream());
                        newOos.writeObject(rMessage);
                    }

                } else if (msgType.equals(MessageType.MESSAGE_FILE_MSG_DIRECT)) {
                    //文件直接传输
                    StoCThread stoCThread = ManageStoCThread.getStoCThread(message.getGetter());//获取收件人的线程
                    ObjectOutputStream oos = new ObjectOutputStream(stoCThread.getSocket().getOutputStream());
                    oos.writeObject(message);
                    System.out.println
                            ("用户: " + message.getSender() + " 向用户 " + message.getGetter() + " 发送了一份文件 " + message.getSrc());

                } else if (msgType.equals(MessageType.MESSAGE_EXIT)) {
                    //退出
                    System.out.println(message.getSender() + " 退出程序");
                    ManageStoCThread.deleteStoCThread(message.getSender());
                    socket.close();

                    //向其他在线用户更新用户列表
                    StringBuilder data = ArisuDB.getAllUserToString();
                    data.append(ManageStoCThread.getOnlineList());
                    //message构造
                    Message returnMessage = new Message();
                    returnMessage.setContent(data.toString());
                    returnMessage.setMesType(MessageType.MESSAGE_SET_ONLINE_LIST);
                    returnMessage.setGetter("服务器");
                    HashMap<String, StoCThread> manage = ManageStoCThread.getManage();
                    Iterator<String> iterator = manage.keySet().iterator();
                    while (iterator.hasNext()) {
                        String next = iterator.next().toString();
                        ObjectOutputStream oos =
                                new ObjectOutputStream(manage.get(next).getSocket().getOutputStream());
                        oos.writeObject(returnMessage);
                    }
                    break;
                } else {

                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public Socket getSocket() {
        return socket;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
}
